package co.recloud.ariadne.request;

import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;

import co.recloud.ariadne.model.Host;
import co.recloud.ariadne.thread.Main;

public class ConnectionPool {
	private Map<Long, Connection> connections;
	private ConcurrentHashMap<String, Queue<Long>> hostToFreeId;
	private static ConnectionPool singleton = null;
	private Semaphore maxConnections;
	public static final Integer MAX_CONNECTIONS = 20;
	
	public static ConnectionPool getInstance() {
		if(singleton == null) {
			singleton = new ConnectionPool();
		}
		return singleton;
	}
	
	public ConnectionPool() {
		connections = new ConcurrentHashMap<Long, Connection>();
		hostToFreeId = new ConcurrentHashMap<String, Queue<Long>>();
		maxConnections = new Semaphore(MAX_CONNECTIONS, true);
	}
	
	public Connection getConnection(Host target) {
		Connection connection = null;
		try {
			String hostId = target.getHostName() + ":" + target.getPort();
			maxConnections.acquire();
			hostToFreeId.putIfAbsent(hostId, new ConcurrentLinkedQueue<Long>());
			Long connectionId = null;
			try {
				connectionId = hostToFreeId.get(hostId).remove();
			} catch (Exception e) {
				
			}
			if(connectionId == null) {
				if(connections.size() >= MAX_CONNECTIONS - 1) {
					for(String otherHostId : hostToFreeId.keySet()) {
						try {
							connectionId = hostToFreeId.get(otherHostId).remove();
						} catch (Exception e) {
							continue;
						}
						if(connectionId != null) {
							connection = connections.get(connectionId);
							try {
								connection.getInputStream().close();
							} catch (IOException e) {
								
							}
							try {
								connection.getOutputStream().close();
							} catch (IOException e) {
							}
							try {
								connection.getClientSocket().close();
							} catch(IOException e) {
								
							}
							break;
						}
					}
				} 
				if (connection == null) {
					connection = new Connection();
					connection.setId(Main.getTime());
					connections.put(connection.getId(), connection);
				}
				connection.setClientSocket(new Socket(target.getHostName(), target.getPort()));
				connection.setTarget(target);
			} else {
				connection = connections.get(connectionId);
			}
		} catch (IOException e) {
			
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return connection;
	}
	
	public void releaseConnection(Connection connection) {
		try {
			String hostId = connection.getTarget().getHostName() + ":" + connection.getTarget().getPort();
			hostToFreeId.get(hostId).add(connection.getId());
		} finally {
			maxConnections.release();
		}
	}
}
